/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.sgf.provider.vo.custom;

import com.inspur.edp.formserver.viewmodel.GspViewModel;
import com.inspur.edp.formserver.viewmodel.action.IViewModelParameter;
import com.inspur.edp.formserver.viewmodel.action.ViewModelAction;
import com.inspur.edp.formserver.viewmodel.common.VMCollectionParameterType;
import com.inspur.edp.formserver.viewmodel.common.VMParameterType;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.sgf.api.entity.ServiceOperation;
import com.inspur.edp.sgf.api.entity.ServiceParameter;
import com.inspur.edp.sgf.api.entity.SgModelType;
import com.inspur.edp.sgf.provider.vo.basic.BasicActionSummary;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * 功能描述: @ClassName: CustomActionProvider @Author: Fynn Qi @Date: 2020/2/3 16:54 @Version: V1.0
 */
@Slf4j
public class CustomActionProvider {

    public static List<ServiceOperation> getCustomActions(GspMetadata gspMetadata) {
        GspViewModel viewModel = (GspViewModel) gspMetadata.getContent();
        List<ViewModelAction> actions = viewModel.getActions();
        return actionToServiceOperation(actions, gspMetadata, viewModel);
    }

    /**
     * 根据ViewModelAction生成ServiceOperation
     *
     * @param actions    ViewModelAction列表
     * @param voMetadata 元数据
     * @param viewModel  vo视图模型
     * @return 生成的ServiceOperation列表，非null
     */
    private static List<ServiceOperation> actionToServiceOperation(List<ViewModelAction> actions,
                                                                   GspMetadata voMetadata, GspViewModel viewModel) {
        List<ServiceOperation> operations = new ArrayList<>();
        //动作编号集合，用于判断该动作编号是否已被添加至eapi的操作列表，避免相同动作编号的动作导致eapi生成代码报错
        Set<String> customActionCodeSet = new HashSet<>();
        for (ViewModelAction action : actions) {
            long count =
                    BasicActionSummary.getBasicActions(voMetadata).stream()
                            .filter(x -> x.getCode().equals(action.getCode()))
                            .count();
            if (count > 0) {
                throw new RuntimeException(
                        String.format(
                                "操作编号%s为预置的基本操作编号，请修改%s[ID:%s]的自定义动作编号",
                                action.getCode(), viewModel.getCode(), viewModel.getID()));
            }
            //检查vo上是否存在相同编号的动作，保证每个编号的动作仅添加一次
            if (customActionCodeSet.contains(action.getCode())) {
                log.error(String.format("根据vo上的自定义动作生成eapi操作时出错，vo【code:%s,ID:%s】上存在相同动作编号（code:%s）的动作，请检查对应vo元数据。该编号的动作仅向eapi添加一个",
                        voMetadata.getHeader().getCode(), voMetadata.getHeader().getId(), action.getCode()));
            } else {
                ServiceOperation operation = new ServiceOperation();
                operation.setId(MessageFormat.format("{0}&^^&{1}", action.getID(), action.getCode()));
                operation.setCode(action.getCode());
                operation.setName(action.getName());
                operation.setResourceOpId(
                        MessageFormat.format("{0}&^^&{1}", action.getID(), action.getCode()));
                operation.setDescription(action.getName());
                operation.setHttpMethod("PUT");
                operation.setTemplate("/service/" + action.getCode().toLowerCase());
                operation.setDeprecated(false);
                operation.setExtend1("Custom");
                operation.setParameters(getParams(action, voMetadata));

                operations.add(operation);
                //添加动作编号至集合中
                customActionCodeSet.add(action.getCode());
            }
        }
        return operations;
    }

    public static List<ServiceParameter> getParams(ViewModelAction action, GspMetadata gspMetadata) {
        List<ServiceParameter> parameters = new ArrayList<>(getInputParams(action));
        ServiceParameter retParam = getActionReturnParam(action, gspMetadata);
        if (retParam != null && StringUtils.isNotBlank(retParam.getId())) {
            parameters.add(retParam);
        }
        return parameters;
    }

    public static List<ServiceParameter> getInputParams(ViewModelAction action) {
        List<ServiceParameter> parameters = new ArrayList<>();
        parameters.add(buildRequestInfoParam(action));
        if (action.getParameterCollection() != null && action.getParameterCollection().getCount() > 0) {
            Iterator<IViewModelParameter> it = action.getParameterCollection().iterator();
            while (it.hasNext()) {
                IViewModelParameter param = it.next();
                ServiceParameter serviceParam = getActionParam(param);
                if (serviceParam != null && StringUtils.isNotBlank(serviceParam.getId())) {
                    parameters.add(serviceParam);
                }
            }
        }
        return parameters;
    }

    public static ServiceParameter buildRequestInfoParam(ViewModelAction action) {
        ServiceParameter requestInfoParam = new ServiceParameter();
        requestInfoParam.setId(MessageFormat.format("{0}&^^&RequestInfo", action.getID()));
        requestInfoParam.setCode("requestInfo");
        requestInfoParam.setName("请求信息");
        requestInfoParam.setDescription("请求信息");
        requestInfoParam.setRetVal(false);
        requestInfoParam.setBasicType(false);
        requestInfoParam.setCollection(false);
        requestInfoParam.setListDeepth(0);
        requestInfoParam.setModelId(MessageFormat.format("{0}&^^&RequestInfo", action.getID()));
        requestInfoParam.setModelCode("RequestInfo");
        requestInfoParam.setModelName("RequestInfo");
        requestInfoParam.setModelType(SgModelType.DTO);
        requestInfoParam.setRefId(MessageFormat.format("{0}&^^&RequestInfo", action.getID()));
        requestInfoParam.setRefCode("RequestInfo");
        requestInfoParam.setRefName("RequestInfo");
        requestInfoParam.setIn("Body");
        requestInfoParam.setIsRequired(false);
        return requestInfoParam;
    }

    private static ServiceParameter getActionReturnParam(
            ViewModelAction action, GspMetadata gspMetadata) {
        ServiceParameter responseInfoParam = new ServiceParameter();
        responseInfoParam.setId(MessageFormat.format("{0}&^^&ResponseInfo", action.getID()));
        responseInfoParam.setCode("responseInfo");
        responseInfoParam.setName("响应信息");
        responseInfoParam.setDescription("响应信息");
        responseInfoParam.setRetVal(true);
        responseInfoParam.setBasicType(false);
        responseInfoParam.setCollection(false);
        responseInfoParam.setListDeepth(0);
        responseInfoParam.setModelId(
                MessageFormat.format("{0}&^^&ResponseInfo", gspMetadata.getHeader().getId()));
        responseInfoParam.setModelCode("ResponseInfo");
        responseInfoParam.setModelName("ResponseInfo");
        responseInfoParam.setModelType(SgModelType.DTO);
        responseInfoParam.setRefId(
                MessageFormat.format("{0}&^^&ResponseInfo", gspMetadata.getHeader().getId()));
        responseInfoParam.setRefCode("ResponseInfo");
        responseInfoParam.setRefName("ResponseInfo");
        responseInfoParam.setIn("");
        responseInfoParam.setIsRequired(true);

        return responseInfoParam;
    }

    private static ServiceParameter getActionParam(IViewModelParameter param) {
        ServiceParameter serviceParam = new ServiceParameter();
        serviceParam.setId(MessageFormat.format("{0}&^^&{1}", param.getID(), param.getParamCode()));
        serviceParam.setCode(param.getParamCode());
        serviceParam.setName(param.getParamName());
        serviceParam.setDescription(param.getParamDescription());
        serviceParam.setRetVal(false);
        serviceParam.setCollection(false);
        serviceParam.setListDeepth(0);

        if (param.getCollectionParameterType() == VMCollectionParameterType.List
                || param.getCollectionParameterType() == VMCollectionParameterType.Array) {
            serviceParam.setCollection(true);
            serviceParam.setListDeepth(1);
        }
        serviceParam.setIn("Body");
        serviceParam.setIsRequired(true);
        boolean isBasicType = isBasicType(param.getParameterType());
        String typeString = getTypeInfo(param.getParameterType());
        if (isBasicType) {
            serviceParam.setBasicType(true);
            serviceParam.setModelId(typeString);
            serviceParam.setModelName(typeString);
        } else {
            serviceParam.setBasicType(false);
            serviceParam.setModelType(SgModelType.DTO);
            serviceParam.setModelId(typeString);
            serviceParam.setModelCode(typeString);
            serviceParam.setModelName(typeString);
            serviceParam.setRefId(typeString);
            serviceParam.setRefCode(typeString);
            serviceParam.setRefName(typeString);
        }
        return serviceParam;
    }

    private static String getTypeInfo(VMParameterType vMParameterType) {
        String typestring = "";
        switch (vMParameterType) {
            case Boolean:
                typestring = "bool";
                break;
            case DateTime:
                typestring = "DateTime";
                break;
            case Decimal:
                typestring = "Decimal";
                break;
            case Double:
                typestring = "double";
                break;
            case Int32:
                typestring = "int";
                break;
            case String:
                typestring = "string";
                break;
            case Object:
            case Custom:
            default:
                typestring = "object";
                break;
        }
        return typestring;
    }

    private static boolean isBasicType(VMParameterType vMParameterType) {
        switch (vMParameterType) {
            case Boolean:
            case DateTime:
            case Decimal:
            case Double:
            case Int32:
            case String:
            case Object:
                return true;
            default:
                return false;
        }
    }
}
